using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading;
using SpoonCore2.Client;
using SpoonCore2.OverlayNetwork.Pastry;
using SpoonCore2.OverlayNetwork.Pastry.Messages;
using SpoonCore2.Routing;
using SpoonCore2.Tools;

namespace SpoonCore2.Routing
{
	public class LeaveTable : ITable
	{
		private const int Size = 10;

		private readonly List<ClientInfo> _down = new List<ClientInfo>();
		private readonly List<ClientInfo> _up = new List<ClientInfo>();
		private readonly PastryNetwork _network;

		public LeaveTable(PastryNetwork network)
		{
			_network = network;
		}

		public void Add(ClientInfo client)
		{
			if (client.Id != _network.ClientInfo.Id)
			{
				Monitor.Enter(_down);
				if (!_down.Contains(client))
				{
					_down.Sort();
					if (_down.Count < Size)
						_down.Add(client);
					else
					{
						ClientInfo last = _down[0];
						int lastIndex = 0;
						for (int x = 1; x < _down.Count; x++)
						{
							if (_network.ClientInfo.Id.NearestDown(_down[x].Id, last.Id) == last.Id)
							{
								last = _down[x];
								lastIndex = x;
							}
						}

						if (_network.ClientInfo.Id.NearestDown(last.Id, client.Id) == client.Id)
						{
							_down.RemoveAt(lastIndex);
							_down.Add(client);
						}
					}
				}
				Monitor.Exit(_down);

				Monitor.Enter(_up);
				if (!_up.Contains(client))
				{
					_up.Sort();
					if (_up.Count < Size)
						_up.Add(client);
					else
					{
						ClientInfo last = _up[0];
						int lastIndex = 0;
						for (int x = 1; x < _up.Count; x++)
						{
							if (_network.ClientInfo.Id.NearestUp(_up[x].Id, last.Id) == last.Id)
							{
								last = _up[x];
								lastIndex = x;
							}
						}

						if (_network.ClientInfo.Id.NearestUp(last.Id, client.Id) == client.Id)
						{
							_up.RemoveAt(lastIndex);
							_up.Add(client);
						}
					}
				}
				Monitor.Exit(_up);
			}
		}

		public ClientInfo FindNext(SpoonIdentifier id)
		{
			ClientInfo res = null;
			if(id < _network.ClientInfo.Id)
			{
				Monitor.Enter(_down);
				foreach (ClientInfo info in _down)
				{
					if (res == null) res = info;
					else if (id.NearestUp(res.Id, info.Id) == info.Id) res = info;
				}
				Monitor.Exit(_down);
			}
			else
			{
				Monitor.Enter(_up);
				foreach (ClientInfo info in _up)
				{
					if (res == null) res = info;
					else if (id.NearestDown(res.Id, info.Id) == info.Id) res = info;
				}
				Monitor.Exit(_up);
			}
			return res;
		}

		public IList<ClientInfo> Clients
		{
			get
			{
				List<ClientInfo> res = new List<ClientInfo>();
				Monitor.Enter(_down);
				for (int x = 0; x < _down.Count; x++)
				{
					if (!res.Contains(_down[x])) res.Add(_down[x]);
				}
				Monitor.Exit(_down);
				Monitor.Enter(_up);
				for (int x = 0; x < _up.Count; x++)
				{
					if (!res.Contains(_up[x])) res.Add(_up[x]);
				}
				Monitor.Exit(_up);
				return res;
			}
		}

		public void Remove(ClientInfo client)
		{
			Monitor.Enter(_down);
			if (_down.Contains(client)) _down.Remove(client);
			Monitor.Exit(_down);

			Monitor.Enter(_up);
			if (_up.Contains(client)) _up.Remove(client);
			Monitor.Exit(_up);
		}

		public bool Check(List<ClientInfo> clients)
		{
			bool status = true;
			IList<ClientInfo> localClients = Clients;

			List<SpoonIdentifier> checkClients = new List<SpoonIdentifier>();
			foreach (ClientInfo client in clients)
			{
				checkClients.Add(client.Id);
			}

			// check if not allready present
			foreach (ClientInfo client in localClients)
			{
				if (!checkClients.Contains(client.Id))
				{
					ColorConsole.WriteLine("Client " + client + " removed");
					status = false;
				}
			}

			return status;
		}

		public void Refresh()
		{
			IList<ClientInfo> clients = Clients;
			//TODO fixen
			/*if ((_up.Count < Size || _down.Count < Size) && clients.Count > 0)
			{
				// send leav request to next node left or right
				ExchangeMessage msg = new ExchangeMessage();
				msg.LeaveSet = true;
				if(new Random().NextDouble() >= 0.5) _up[0].Send(msg);
				else _down[_down.Count-1].Send(msg);
			}*/
		}
	}
}